package com.atguigu.gulimail.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.To.MemberOrderItemTo;
import com.atguigu.common.To.OrderItemTo;
import com.atguigu.common.To.OrderTo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberResponseVo;
import com.atguigu.gulimail.order.constant.OrderConstant;
import com.atguigu.gulimail.order.dao.OrderDao;
import com.atguigu.gulimail.order.entity.OrderEntity;
import com.atguigu.gulimail.order.entity.OrderItemEntity;
import com.atguigu.gulimail.order.entity.PaymentInfoEntity;
import com.atguigu.gulimail.order.enume.OrderStatusEnum;
import com.atguigu.gulimail.order.pay.vo.PayAsyncVo;
import com.atguigu.gulimail.order.service.OrderItemService;
import com.atguigu.gulimail.order.service.OrderService;
import com.atguigu.gulimail.order.web.To.FareTo;
import com.atguigu.gulimail.order.web.To.OrderCreateTo;
import com.atguigu.gulimail.order.web.To.SpuInfoTo;
import com.atguigu.gulimail.order.web.feign.CartFeign;
import com.atguigu.gulimail.order.web.feign.MemberFeign;
import com.atguigu.gulimail.order.web.feign.ProductFeign;
import com.atguigu.gulimail.order.web.feign.WareFeign;
import com.atguigu.gulimail.order.web.intercept.OrderInterceptor;
import com.atguigu.gulimail.order.web.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.seata.core.context.RootContext;
import io.seata.core.exception.TransactionException;
import io.seata.spring.annotation.GlobalTransactional;
import io.seata.tm.api.GlobalTransactionContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    ProductFeign productFeign;
    @Autowired
    MemberFeign memberFeign;
    @Autowired
    CartFeign cartFeign;
    @Autowired
    WareFeign wareFeign;
    @Autowired
    ThreadPoolExecutor executor;
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    OrderItemService orderItemService;
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单详情页的数据Vo
     * @return
     */
    @Override
    public OrderConfirmVO getOrderConfirmVo() {
        RequestAttributes requestAttributes= RequestContextHolder.getRequestAttributes();

        MemberResponseVo vo = OrderInterceptor.threadLocal.get();
        Long userId = vo.getId();

        OrderConfirmVO orderConfirmVO = new OrderConfirmVO();
        //设置用户积分
        orderConfirmVO.setIntegration(vo.getIntegration());


        CompletableFuture<Void> getAddress=CompletableFuture.runAsync(()->{
            //共享请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //通过远程调用查询用户的收货地址信息；
            List<MemberAddressVo> memberAddress = memberFeign.getMemberAddress(userId);
            orderConfirmVO.setMemberAddressVos(memberAddress);
        },executor);

        CompletableFuture<Void> getCartItems=CompletableFuture.runAsync(()->{
            //共享请求数据
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //通过远程调用查询用户购物车中选中的sku信息
            List<OrderItemVo> userCartItems = cartFeign.getUserCartItems();
            orderConfirmVO.setItems(userCartItems);
        },executor).thenRunAsync(()->{
            List<OrderItemVo> itemVos=orderConfirmVO.getItems();
            List<Long> skuIds = itemVos.stream().map(i -> i.getSkuId()).collect(Collectors.toList());
            List<SkuStockVo> skuHasStockList = wareFeign.getSkuHasStockList(skuIds);

            if(skuHasStockList!=null){
                Map<Long,Boolean> map=new HashMap<>();
                for (SkuStockVo s :
                        skuHasStockList) {
                    map.put(s.getSkuId(),s.getStock());
                }
                orderConfirmVO.setStocks(map);
            }
        });


        try {
            CompletableFuture.allOf(getAddress,getCartItems).get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //TODO 接口幂等性
        //1. 设置订单提交的防重令牌
        String uuid = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVO.setOrderToken(uuid);//给前端返回uuid
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX+userId,uuid,30, TimeUnit.MINUTES);//Redis存储uuid 存储格式为 pre+"userId": uuid


        return orderConfirmVO;
    }

    /**
     * 提交订单
     * @param orderSubmitVo
     * @return
     */
    @Override
    @GlobalTransactional
    @Transactional
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) throws TransactionException {
        MemberResponseVo vo = OrderInterceptor.threadLocal.get();
        Long userId = vo.getId();

        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        //下单，创建订单，验证令牌，确认价格，锁库存
        //1. 验证令牌，比较Redis中的令牌和请求中的令牌，令牌的获取，验证，删除必须为原子性，所以使用Lua脚本
        //该脚本返回值为0：失败，1：成功
        String orderToken=orderSubmitVo.getOrderToken();
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long result = (Long) redisTemplate.execute(
                new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + userId),
                orderToken);

        if(result==0){
            //幂等令牌验证失败
            responseVo.setCode(1);
            return responseVo;

        }else {
            //成功
            //1. 创建订单,订单项等信息
            OrderCreateTo orderCreateTo=orderCreateTo(orderSubmitVo);

            //2. 验价
                //这个是根据数据库查询得到的价格
            BigDecimal payAmount = orderCreateTo.getOrder().getPayAmount();
                //这个是页面提交过来的金额
            BigDecimal payPrice = orderSubmitVo.getPayPrice();
                //我们认为，两个价格相差不超过0.01即为验价成功
            if(Math.abs(payPrice.subtract(payAmount).doubleValue())<0.01){
                //成功,保存订单
                saveOrder(orderCreateTo);
                //订单保存成功进行锁库存; 有异常，回滚
                //锁库存需要的数据： orderId--订单id  |  SkuId--sku名称/id  |  SkuNum--sku的数量  对应Vo：WareSkuLockVo

                WareSkuLockVo wareSkuLockVo=new WareSkuLockVo();

                wareSkuLockVo.setOrderSn(orderCreateTo.getOrder().getOrderSn());

                List<OrderItemVo> collect = orderCreateTo.getOrderItems().stream().map(item -> {
                    OrderItemVo orderItemVo = new OrderItemVo();

                    orderItemVo.setCount(item.getSkuQuantity());
                    orderItemVo.setSkuId(item.getSkuId());
                    orderItemVo.setTitle(item.getSkuName());
                    orderItemVo.setPrice(item.getSkuPrice());

                    return orderItemVo;
                }).collect(Collectors.toList());

                wareSkuLockVo.setLocks(collect);

                //远程调用锁库存
                R r = wareFeign.orderLocakStock(wareSkuLockVo);
                if(r.getCode()==0){
                    responseVo.setOrder(orderCreateTo.getOrder());
                    //锁定成功
                    log.info("Seata全局事务id=================>{}", RootContext.getXID());
                    //TODO 发送消息给MQ，订单创建成功
                    try{
                        //TODO 保证消息一定会发送出去，每一个消息都做好日志记录（mq_message）
                        //TODO 定期扫描数据库，将消息重新发送一遍
                        OrderEntity order = orderCreateTo.getOrder();
                        rabbitTemplate.convertAndSend("order-event-exchange","order.create.order",order);

                    }catch (Exception e){
                        //设法将消息重新发送

                    }
                    responseVo.setCode(0);
                    return responseVo;
                }else {
                    //锁定失败
                    responseVo.setCode(3);
                    throw new RuntimeException("锁定库存异常");
                    //return responseVo;
                }

            }else {
                throw new RuntimeException("验价格失败");
            }
        }

    }

    /**
     * 关闭过期订单；
     * @param orderEntity
     */
    @Override
    public void closeOrder(OrderEntity orderEntity) {

        //拿到消息队列发送的订单信息，去数据库查询订单
        OrderEntity order = this.getById(orderEntity.getId());
        //只有当订单的状态为“待付款”时候，才能将订单的信息设置为“已取消”
        if(order.getStatus()==OrderStatusEnum.CREATE_NEW.getCode()){
            OrderEntity entity = new OrderEntity();
            entity.setId(order.getId());
            entity.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(entity);
        }
        //订单取消成功，主动发消息给解锁库存队列，防止由于网络延迟导致，库存解锁服务先与订单取消服务，导致库存解锁时查询订单状态为待支付，解锁失败
        OrderTo orderTo = new OrderTo();
        BeanUtils.copyProperties(order,orderTo);
        rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);

    }

    @Override
    public OrderEntity getByOrderSn(String orderSn) {
        LambdaQueryWrapper<OrderEntity> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(OrderEntity::getOrderSn,orderSn);

        return this.getOne(wrapper);
    }



    /**
     * 保存订单的所有数据
     * @param order
     */
    private void saveOrder(OrderCreateTo order) {
        //保存订单
        OrderEntity orderEntity= order.getOrder();
        //设置修改时间
        orderEntity.setModifyTime(new Date());
        orderEntity.setCreateTime(new Date());
        this.save(orderEntity);
        //保存订单项
        List<OrderItemEntity> orderItems = order.getOrderItems();
        orderItemService.saveBatch(orderItems);

    }

    //获取订单详情页面的数据；
    private OrderCreateTo orderCreateTo(OrderSubmitVo orderSubmitVo){

        //生成订单号
        String orderSn= IdWorker.getTimeId();

        //设置订单运费,地址，收货人，等信息
        OrderCreateTo to=buildOrder(orderSubmitVo,orderSn);

        //远程查询，购物车中的订单项的信息
        List<OrderItemEntity> orderItemEntities = buildOrderItems(orderSn);
        to.setOrderItems(orderItemEntities);

        to=computePrice(to);
        return to;
    }

    private OrderCreateTo computePrice(OrderCreateTo to) {
        //总价
        BigDecimal total = new BigDecimal("0.0");
        //优惠价
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal intergration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        //积分、成长值
        Integer integrationTotal = 0;
        Integer growthTotal = 0;

        //首先计算订单价格
            //将数据项的实际金额加起来
            //拿出订单积分数据，打折数据，优惠数据
        List<OrderItemEntity> orderItems = to.getOrderItems();
        for (OrderItemEntity item:
             orderItems) {
            total=total.add(item.getRealAmount());
            coupon=coupon.add(item.getCouponAmount());
            intergration=intergration.add(item.getIntegrationAmount());
            promotion=promotion.add(item.getPromotionAmount());
            //积分信息和成长值信息
            integrationTotal += item.getGiftIntegration();
            growthTotal += item.getGiftGrowth();

        }
        //设置积分成长值信息
        to.getOrder().setIntegration(integrationTotal);
        to.getOrder().setGrowth(growthTotal);

        to.getOrder().setTotalAmount(total);
        //计算订单的应付金额= 数据项金额 + 运费
        to.getOrder().setPayAmount(total.add(to.getFare()));

        //设置删除状态(0-未删除，1-已删除)
        to.getOrder().setDeleteStatus(0);
        return to;
    }

    private OrderCreateTo buildOrder(OrderSubmitVo orderSubmitVo,String  orderSn) {
        OrderCreateTo to=new OrderCreateTo();

        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);

        //远程查询订单运费,地址，收货人，等信息
        Object o = wareFeign.getFare(orderSubmitVo.getAddrId()).get("data");

        String jsonString = JSON.toJSONString(o);
        FareTo fareTo=JSON.parseObject(jsonString,FareTo.class);
        MemberAddressVo address = fareTo.getAddress();

        //设置运费信息
        to.setFare(fareTo.getFare());

        //设置收货人信息
        orderEntity.setReceiverName(address.getName());
        orderEntity.setReceiverPhone(address.getPhone());
        orderEntity.setReceiverPostCode(address.getPostCode());
        orderEntity.setReceiverProvince(address.getProvince());
        orderEntity.setReceiverCity(address.getCity());
        orderEntity.setReceiverRegion(address.getRegion());
        orderEntity.setReceiverDetailAddress(address.getDetailAddress());

        //设置定单的状态为待付款
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        //设置订单的默认数据
        orderEntity.setConfirmStatus(7);
        //设置订单的memberId
        MemberResponseVo vo = OrderInterceptor.threadLocal.get();
        orderEntity.setMemberId(vo.getId());
        orderEntity.setMemberUsername(vo.getUsername());
        to.setOrder(orderEntity);

        return to;

    }


    /**
     * @param orderSn 订单号
     * @return
     */
    private List<OrderItemEntity> buildOrderItems(String orderSn) {

        List<OrderItemVo> userCartItems = cartFeign.getUserCartItems();
        if(userCartItems!=null&&userCartItems.size()>0){
            List<OrderItemEntity> collect = userCartItems.stream().map(item -> {
                OrderItemEntity itemEntity=buildOrderItem(item,orderSn);
                return itemEntity;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;
    }

    /**
     * 构建订单项
     * @param item
     * @return
     */
    private OrderItemEntity buildOrderItem(OrderItemVo item,String orderSn) {
        OrderItemEntity orderItemEntity = new OrderItemEntity();
        //拿到购物项的skuId
        Long skuId = item.getSkuId();

        //订单信息： 订单号 v
        orderItemEntity.setOrderSn(orderSn);
        //通过skuId远程查询商品spu信息
        R r = productFeign.infoBySkuId(skuId);
        Object o = r.get("data");
        String spuStr=JSON.toJSONString(o);
        SpuInfoTo spuInfoTo = JSON.parseObject(spuStr, SpuInfoTo.class);
        orderItemEntity.setSpuBrand(spuInfoTo.getBrandId().toString());
        orderItemEntity.setSpuId(spuInfoTo.getId());

        orderItemEntity.setCategoryId(spuInfoTo.getCatalogId());
        orderItemEntity.setSpuName(spuInfoTo.getSpuName());
        //商品Sku信息
        //2、商品的sku信息
        orderItemEntity.setSkuId(skuId);
        orderItemEntity.setSkuName(item.getTitle());
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuQuantity(item.getCount());
        //使用StringUtils.collectionToDelimitedString将list集合转换为String
        String skuAttrValues = StringUtils.collectionToDelimitedString(item.getSkuAttrValues(), ";");
        orderItemEntity.setSkuAttrsVals(skuAttrValues);

        //优惠信息【不做】
        //积分信息
        //4、商品的积分信息
        orderItemEntity.setGiftGrowth(item.getPrice().multiply(new BigDecimal(item.getCount())).intValue());
        orderItemEntity.setGiftIntegration(item.getPrice().multiply(new BigDecimal(item.getCount())).intValue());

        orderItemEntity.setPromotionAmount(BigDecimal.ZERO);//设置优惠为0
        orderItemEntity.setCouponAmount(BigDecimal.ZERO);//设置打折为0
        orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);//设置积分为0

        //5、订单项的价格信息
        //当前订单项的实际金额.总额 - 各种优惠价格
        //原来的价格
        BigDecimal origin = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
        //原价减去优惠价得到最终的价格
        BigDecimal subtract = origin.subtract(orderItemEntity.getCouponAmount())//减去优惠
                .subtract(orderItemEntity.getPromotionAmount())//减去打折
                .subtract(orderItemEntity.getIntegrationAmount());//减去积分

        //设置定单项的真实金额
        orderItemEntity.setRealAmount(subtract);

        return orderItemEntity;
    }

/*
    会员订单列表查询
 */
    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberResponseVo memberResponseVo= OrderInterceptor.threadLocal.get();

        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
                        .eq("member_id",memberResponseVo.getId())
                        .orderByDesc("id")
        );

        List<MemberOrderItemTo> collect = page.getRecords().stream().map(i -> {
            String orderSn = i.getOrderSn();
            LambdaQueryWrapper<OrderItemEntity> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(OrderItemEntity::getOrderSn, orderSn);
            List<OrderItemEntity> list = orderItemService.list(wrapper);

            List<OrderItemTo> items = list.stream().map(item -> {
                OrderItemTo orderItemTo = new OrderItemTo();
                BeanUtils.copyProperties(item, orderItemTo);
                return orderItemTo;
            }).collect(Collectors.toList());

            MemberOrderItemTo memberOrderItemTo = new MemberOrderItemTo();
            BeanUtils.copyProperties(i,memberOrderItemTo);
            memberOrderItemTo.setOrderItemEntityList(items);

            return memberOrderItemTo;
        }).collect(Collectors.toList());

        PageUtils pageUtils = new PageUtils(page);
        pageUtils.setList(collect);
        return pageUtils;

    }

    /**
     * 处理支付宝支付成功返回结果
     * @param payAsyncVo
     * @return
     */
    @Override
    public String handlePayResult(PayAsyncVo payAsyncVo) {
        //保存交易流水
        PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();
        paymentInfoEntity.setAlipayTradeNo(payAsyncVo.getTrade_no());
        paymentInfoEntity.setOrderSn(payAsyncVo.getOut_trade_no());
        paymentInfoEntity.setPaymentStatus(payAsyncVo.getTrade_status());
        paymentInfoEntity.setCallbackTime(payAsyncVo.getNotify_time());

        //修改订单状态信息
        if(payAsyncVo.getTrade_status().equals("TRADE_SUCCESS")||payAsyncVo.getTrade_status().equals("TRADE_FINISHED"))
        {
            String outTradeNo = payAsyncVo.getOut_trade_no();
            baseMapper.updateOrderStatus(outTradeNo,OrderStatusEnum.PAYED.getCode());
            return "success";
        }
        return null;
    }

}